﻿namespace HZY.Framework.DynamicApiController.Core;

/// <summary>
/// 应用程序模型约定转换处理
/// </summary>
public class DynamicApiControllerApplicationModelConvention : IApplicationModelConvention
{
    private readonly string _controllerRule = "[controller]";
    private readonly string _actionRule = "[action]";
    private readonly string _asyncString = "Async";
    private readonly DynamicApiControllerOptions _dynamicApiControllerOptions;
    private readonly MvcOptions _mvcOptions;

    /// <summary>
    /// 应用程序模型约定转换处理
    /// </summary>
    /// <param name="dynamicApiControllerOptions">配置参数</param>
    /// <param name="mvcOptions">mvc 参数配置</param>
    public DynamicApiControllerApplicationModelConvention(DynamicApiControllerOptions dynamicApiControllerOptions,
        MvcOptions mvcOptions)
    {
        _dynamicApiControllerOptions = dynamicApiControllerOptions;
        _mvcOptions = mvcOptions;
    }

    /// <summary>
    /// 使用
    /// </summary>
    /// <param name="application"></param>
    public virtual void Apply(ApplicationModel application)
    {
        var controllers = application.Controllers
                .Where(w => w is not null)
            //排除 Controller 结尾的文件
            //.Where(w => !w.ControllerType.Name.EndsWith(DynamicApiControllerTypeNameSuffixEnum.Controller.ToString(), StringComparison.OrdinalIgnoreCase))
            ;

        foreach (var item in controllers)
        {
            RemoveEmptySelectors(item.Selectors);

            HandleControllerName(item);

            foreach (var action in item.Actions.Where(w => w is not null))
            {
                RemoveEmptySelectors(action.Selectors);

                HandleActionRoute(action);
            }
        }
    }

    /// <summary>
    /// 移除空的选择器 防止干扰
    /// </summary>
    /// <param name="selectors"></param>
    private void RemoveEmptySelectors(IList<SelectorModel> selectors)
    {
        for (var i = selectors.Count - 1; i >= 0; i--)
        {
            var selector = selectors[i];
            if (selector.AttributeRouteModel == null &&
                (selector.ActionConstraints == null || selector.ActionConstraints.Count <= 0) &&
                (selector.EndpointMetadata == null || selector.EndpointMetadata.Count <= 0))
            {
                selectors.Remove(selector);
            }
        }
    }

    /// <summary>
    /// 处理控制器名称
    /// </summary>
    /// <param name="controllerModel"></param>
    private void HandleControllerName(ControllerModel controllerModel)
    {
        controllerModel.ControllerName = GetControllerName(controllerModel.ControllerName);
    }

    /// <summary>
    /// 处理 Action
    /// </summary>
    /// <param name="actionModel"></param>
    private void HandleActionRoute(ActionModel actionModel)
    {
        if (actionModel.Selectors.Count <= 0)
        {
            // 创建路由选择器
            var template = GetRouteTemplate(actionModel);
            var selector = new SelectorModel
            {
                AttributeRouteModel = new AttributeRouteModel(template)
            };
            selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { GetHttpMethod(actionModel) }));
            actionModel.Selectors.Add(selector);

            return;
        }

        // 变更路由选择器
        foreach (var selectorModel in actionModel.Selectors)
        {
            //如果 action 找不到路由标签
            if (selectorModel.AttributeRouteModel == null)
            {
                selectorModel.AttributeRouteModel = new AttributeRouteModel(GetRouteTemplate(actionModel));
            }
            else
            {
                selectorModel.AttributeRouteModel =
                    GetAttributeRouteModel(actionModel, selectorModel.AttributeRouteModel);
            }

            var httpMethodActionConstraint = selectorModel.ActionConstraints.OfType<HttpMethodActionConstraint>();
            if (httpMethodActionConstraint?.FirstOrDefault()?.HttpMethods?.FirstOrDefault() == null)
            {
                selectorModel.ActionConstraints.Add(
                    new HttpMethodActionConstraint(new[] { GetHttpMethod(actionModel) }));
            }
        }
    }

    /// <summary>
    /// 获取 AttributeRouteModel
    /// </summary>
    /// <param name="actionModel"></param>
    /// <param name="attributeRouteModel"></param>
    /// <returns></returns>
    private AttributeRouteModel GetAttributeRouteModel(ActionModel actionModel, AttributeRouteModel attributeRouteModel)
    {
        // 处理 HttpGet(Name="user") （处理 带有 Name 属性）
        var routeAttribute = GetRouteAttributeByControllerRouteAttribute(actionModel);

        if (routeAttribute == null)
            return attributeRouteModel;

        return new AttributeRouteModel(routeAttribute);
    }

    /// <summary>
    /// 获取路由模板地址
    /// </summary>
    /// <param name="actionModel"></param>
    /// <returns></returns>
    private RouteAttribute GetRouteTemplate(ActionModel actionModel)
    {
        // 如果 action 上面已经有了 路由标签
        var actionRouteAttribute = actionModel.Attributes?.FirstOrDefault(w => w is RouteAttribute) as RouteAttribute;
        if (actionRouteAttribute != null)
        {
            return actionRouteAttribute;
        }

        // 如果 action 找不到路由标签 但是 控制器上面有路由标签 (如果是自定义路由)
        var routeAttribute = GetRouteAttributeByControllerRouteAttribute(actionModel);
        if (routeAttribute != null)
        {
            return routeAttribute;
        }

        // 返回默认路由
        return GetDefaultApiPrefixRouteAttribute(actionModel);
    }

    /// <summary>
    /// 根据控制器路由特性获取新的路由特性标记对象
    /// </summary>
    /// <param name="actionModel"></param>
    /// <returns></returns>
    private RouteAttribute? GetRouteAttributeByControllerRouteAttribute(ActionModel actionModel)
    {
        // 检查 actionModel 是否有 HttpMethodAttribute
        var httpMethodAttributes = GetHttpMethodAttributes(actionModel);
        if (httpMethodAttributes is null || httpMethodAttributes.Count == 0)
        {
            return default;
        }

        // Controller
        var controllerName = actionModel.Controller.ControllerName;
        // Action
        var actionNameInfo = GetActionName(actionModel);

        var controllerRouteAttribute =
            actionModel.Controller.Attributes.FirstOrDefault(w => w is RouteAttribute) as RouteAttribute;
        if (controllerRouteAttribute == null)
        {
            controllerRouteAttribute = GetDefaultApiPrefixRouteAttribute(actionModel);
        }

        var routeTemplate = controllerRouteAttribute.Template;
        if (routeTemplate == null) return default;

        if (!routeTemplate.Contains(_actionRule)) return default;

        var actionName = actionNameInfo.AliasName ?? actionNameInfo.ActionName;

        //处理 控制器
        if (!string.IsNullOrWhiteSpace(_controllerRule))
        {
            routeTemplate = routeTemplate.Replace(_controllerRule, controllerName);
        }

        //处理 action 标记
        if (!string.IsNullOrWhiteSpace(actionName))
        {
            routeTemplate = routeTemplate.Replace(_actionRule, actionName);
        }

        //处理 带有 http method 标记
        var httpMethodAttribute = httpMethodAttributes?.FirstOrDefault();
        if (httpMethodAttribute != null && !string.IsNullOrWhiteSpace(httpMethodAttribute.Template))
        {
            routeTemplate += (httpMethodAttribute.Template.StartsWith('/') ? "" : "/") + httpMethodAttribute.Template;
        }

        var routeAttribute = new RouteAttribute(routeTemplate);
        routeAttribute.Name = actionNameInfo.AliasName != null ? actionNameInfo.AliasName : null;
        routeAttribute.Order = controllerRouteAttribute.Order;
        return routeAttribute;
    }

    /// <summary>
    /// 获取api默认前缀 路由对象
    /// </summary>
    /// <param name="actionModel"></param>
    /// <returns></returns>
    private RouteAttribute GetDefaultApiPrefixRouteAttribute(ActionModel actionModel)
    {
        // Controller
        var controllerName = actionModel.Controller.ControllerName;
        // Action
        var actionNameInfo = GetActionName(actionModel);

        // 路由前缀
        return new RouteAttribute($"/{controllerName}/{actionNameInfo.AliasName ?? actionNameInfo.ActionName}");
    }

    /// <summary>
    /// 获取 http method 请求方式
    /// </summary>
    /// <param name="actionModel"></param>
    /// <returns></returns>
    private string GetHttpMethod(ActionModel actionModel)
    {
        var actionName = actionModel.ActionName.ToLower();
        var result = nameof(DynamicApiControllerHttpMethodEnum.POST);

        foreach (var item in _dynamicApiControllerOptions.HttpMethodRule)
        {
            if (actionName.StartsWith(item.Key))
            {
                result = item.Value.ToString();
                break;
            }
        }

        return result;
    }

    /// <summary>
    /// 获取控制器名称
    /// </summary>
    /// <param name="controllerName"></param>
    /// <returns></returns>
    private string GetControllerName(string controllerName)
    {
        var result = controllerName;
        foreach (var item in _dynamicApiControllerOptions.ControllerTypeNameSuffixs)
        {
            if (!controllerName.EndsWith(item, StringComparison.OrdinalIgnoreCase)) continue;
            result = controllerName[..^item.Length];
        }

        if (_dynamicApiControllerOptions.IsLower)
        {
            return DynamicApiControllerExtensions.HumpTurnBar(result);
        }

        return result;
    }

    /// <summary>
    /// 获取action 名称
    /// </summary>
    /// <param name="actionModel"></param>
    /// <returns>AliasName=别名 、ActionName=当前action名称</returns>
    private (string ActionName, string? AliasName) GetActionName(ActionModel actionModel)
    {
        var httpMethodAttributes = GetHttpMethodAttributes(actionModel);
        var name = httpMethodAttributes?.FirstOrDefault()?.Name;
        var actionName = actionModel.ActionName;

        if (_mvcOptions.SuppressAsyncSuffixInActionNames && actionName.EndsWith(_asyncString))
        {
            actionName = actionName[..^_asyncString.Length];
        }

        if (_dynamicApiControllerOptions.IsLower)
        {
            return (DynamicApiControllerExtensions.HumpTurnBar(actionName), name);
        }

        return (actionName, name);
    }

    /// <summary>
    /// 获取 action 上面的http method 特性
    /// </summary>
    /// <param name="actionModel"></param>
    /// <returns></returns>
    private List<HttpMethodAttribute>? GetHttpMethodAttributes(ActionModel actionModel)
    {
        return actionModel.Attributes?
                .Where(w => w is HttpMethodAttribute)
                .OrderByDescending(w => ((HttpMethodAttribute)w).Order)
                .Select(w => (HttpMethodAttribute)w)
                .ToList()
            ;
    }
}